package web

import (
	"sync"
	"time"

	"commonUtils/utils"

	"github.com/gorilla/websocket"
)

// Conn 客户端连接
type Conn struct {
	conn *websocket.Conn
	WsConfig

	server          *Server
	writeBufferChan chan []byte

	messages *utils.ListItem
	muMsg    sync.RWMutex
	ID       string
	Closed   bool
	IP       string
}

var (
	Newline = []byte{'\n'}
	space   = []byte{' '}
)

// ReadProc 读取过程
func (c *Conn) ReadProc() {
	defer func() {
		// c.channel.unregister <- c
		if nil != c.writeBufferChan {
			close(c.writeBufferChan)
			c.writeBufferChan = nil
		}
		if c.Closed {
			return
		}
		c.Closed = true
		c.Close()
	}()
	c.conn.SetPongHandler(func(string) error {
		// utils.Info("pong recived!")
		c.conn.SetReadDeadline(time.Now().Add(c.PongWait))
		return nil
	})
	for {
		c.conn.SetReadDeadline(time.Now().Add(c.PongWait))
		t, message, err := c.conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				if c.ErrorHandleFunc != nil {
					c.ErrorHandleFunc(c, err)
				}
			}
			utils.Debug("read error: %v", err)
			return
		}
		if t == websocket.BinaryMessage {
			if c.DecompressFunc != nil {
				message, err = c.DecompressFunc(message)
				if err != nil {
					utils.Debug("[%s] decompress error %s", c.ID, err.Error())
					continue
				}
			}
		}

		// message = bytes.TrimSpace(bytes.Replace(message, Newline, space, -1))
		// c.channel.broadcast <- message
		if nil != c.ProtoHandleFunc {
			c.ProtoHandleFunc(c, message)
		} else {
			utils.Debug("[%s] ProtoHandleFunc is nil ", c.ID)
		}
	}
}

// WriteProc 写入过程
func (c *Conn) WriteProc() {
	ticker := time.NewTicker(c.PingPeriod)
	defer func() {
		ticker.Stop()
		if c.Closed {
			return
		}
		c.Closed = true
		c.Close()
	}()

	for {
		select {
		case <-ticker.C:
			var err error
			// for i := 0; i < 5; i++ {
			c.conn.SetWriteDeadline(time.Now().Add(c.WriteWait))
			if err = c.conn.WriteMessage(websocket.PingMessage, nil); err == nil {
				break
			}
			// }
			if err != nil {
				utils.Error("ping send error : %s", err)
				return
			} else {
				// utils.Info("ping send success!")
			}
		case message, ok := <-c.writeBufferChan:
			if !ok {
				// The hub closed the channel.
				c.conn.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}
			// length := len(message)
			// utils.Debug("send %d - >", length)
			c.send(message)
			// utils.Debug("< - %d sended", length)
		}
	}
}

func (c *Conn) send(message []byte) {
	c.conn.SetWriteDeadline(time.Now().Add(c.WriteWait))
	w, err := c.conn.NextWriter(websocket.TextMessage)
	if err != nil {
		return
	}
	w.Write(message)

	// Add queued chat messages to the current websocket message.
	n := len(c.writeBufferChan)
	for i := 0; i < n; i++ {
		c.conn.SetWriteDeadline(time.Now().Add(c.WriteWait))
		w.Write(Newline)
		w.Write(<-c.writeBufferChan)
	}

	if err := w.Close(); err != nil {
		utils.Error("writer close error : %s", err)
		return
	}

}

// func (c *Conn) sendNext() {
// 	c.muMsg.Lock()
// 	count := 0
// 	if nil != c.messages {
// 		var m *utils.ListItem
// 		m, c.messages = c.messages.Pop()
// 		c.writeBufferChan <- m.Data.([]byte)

// 		if nil != c.messages {
// 			count = c.messages.Count()
// 		}
// 	}
// 	utils.Debug("messages left count : %d", count)
// 	c.muMsg.Unlock()
// }

// Close 关闭
func (c *Conn) Close() {
	c.conn.Close()
	c.server.DeleteConn(c)
}

func (c *Conn) SendActionData(action string, data interface{}) error {
	return c.SendMsg(CreateMsg(action, data))
}

func (c *Conn) SendMsg(msg *Msg) error {
	data, err := utils.ToByte(msg)
	if err != nil {
		return err
	}
	c.Send(data)
	return nil
}

func (c *Conn) Send(d []byte) {
	defer func() {
		if r := recover(); r != nil {
			utils.Error("send error : %s", r)
		}
	}()
	if !c.Closed {
		c.writeBufferChan <- d
	}
	// c.muMsg.Lock()
	// defer c.muMsg.Unlock()
	// if nil == c.messages {
	// 	c.messages = &utils.ListItem{
	// 		Data: d,
	// 	}
	// } else {
	// 	c.messages.InsertToEnd(&utils.ListItem{
	// 		Data: d,
	// 	})
	// }
}

func (c *Conn) Api(action string, data interface{}) {
	c.Send(CreateMsgByte(action, data))
}
